#include <sstream>
#include <iomanip>
#include <mpi.h>

#include "../Input/PicParams.h"
#include "../Parallel/ParallelMPI.h"
#include "../PhysicalField/PhysicalField.h"
#include "../Species/Species.h"
#include "../IO/IO.h"
#include "../ParticleSource/ParticleSource.h"
#include "../PSI/PSI.h"
#include "../Collision/Collision.h"

using namespace std;


IO::IO(PicParams* params_, ParallelMPI* pmpi_) :
    params(params_),
    pmpi(pmpi_)
{
    restore_file_dir = "restore";

    step_output_max = params->timestep_simulation / params->timestep_io;
}

IO::~IO()
{
    //H5Fclose( data_file_id );
}

void IO::init(Grid* grid_, PhysicalField* fields_, vector<Species*>* vecSpecies_, vector<ParticleSource*>* vecParticleSource_, vector<Collision*>* vecCollision_, vector<PSI*>* vecPSI_, Diagnostic* diag_)
{
    grid = grid_;
    fields = fields_;
    vecSpecies = vecSpecies_;
    vecParticleSource = vecParticleSource_;
    vecCollision = vecCollision_;
    vecPSI = vecPSI_;

    step_start = 0;

    reloadP();

    if( pmpi->isMaster() )
    {
        createFieldsGroup();
    }

    if(pmpi->isMaster() && grid != NULL && grid->grid_type != "from_file")
    {
        writeGrid(grid);
    }
}

void IO::addField(Field<double>* field)
{
    fieldsGroup.dataset_stringName.push_back(field->name);
    fieldsGroup.dataset_data.push_back(field->data_);
}


/* Create restore h5 file pattern
void IO::storeP( PicParams* params_, ParallelMPI* pmpi_, vector<Species*>& vecSpecies, int itime )
{
    Species     *s1;
    Particles   *p1;
    hid_t       group_id;
    hid_t       dataspace_id;
    hid_t       dataset_id;
    hid_t       memspace_id;
    hid_t       attribute_id;
    hid_t       prop;
    herr_t      status;

    hsize_t     count[1];
    hsize_t     offset[1];
    hsize_t     stride[1];
    hsize_t     block[1];

    int restart;
    int partNumber;
    int nbins;
    int timestep;
    int RANK;
    double data_temp;
    hsize_t dims[1];
    hsize_t maxdims[1];
    hsize_t chunk_dims[1];

    RANK = 1;
    maxdims[0] = H5S_UNLIMITED;
    chunk_dims[0] = 2;
    restart = 1;
    timestep = itime;
    data_temp = 66.0;
    nSpecies = vecSpecies.size();

    long long mpi_rk = pmpi->getRank();
    string fileName = restore_file_dir + "/Restore" + to_string( mpi_rk ) + "_global.h5";

    //Restore000_global.h5
    restore_file_id_  = H5Fcreate( fileName.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);

    //========================== Write info about restart ============================
    string group_name = "/info" ;
    group_id = H5Gcreate(restore_file_id_, group_name.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);

    //write the variable "restart"
    dims[0]     = 1;
    count[0]    = 1;
    offset[0]   = 0;
    stride[0]   = 0;
    block[0]    = 0;
    dataspace_id = H5Screate_simple(RANK, dims, NULL);
    dataset_id = H5Dcreate2(group_id, "restart", H5T_NATIVE_INT, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
    status = H5Dwrite (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &restart);
    H5Sclose (dataspace_id);
    H5Dclose (dataset_id);

    //write the variable "timestep"
    dims[0]     = 1;
    count[0]    = 1;
    offset[0]   = 0;
    stride[0]   = 0;
    block[0]    = 0;
    dataspace_id = H5Screate_simple(RANK, dims, NULL);
    dataset_id = H5Dcreate2(group_id, "timestep", H5T_NATIVE_INT, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
    status = H5Dwrite (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &timestep);
    H5Sclose( dataspace_id );
    H5Dclose( dataset_id );

    //write the variable "nSpecies"
    dims[0]     = 1;
    count[0]    = 1;
    offset[0]   = 0;
    stride[0]   = 0;
    block[0]    = 0;
    dataspace_id = H5Screate_simple(RANK, dims, NULL);
    dataset_id = H5Dcreate2(group_id, "nSpecies", H5T_NATIVE_INT, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
    status = H5Dwrite (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &nSpecies);
    H5Sclose( dataspace_id );
    H5Dclose( dataset_id );

    H5Gclose( group_id );

    //========================== Write all species ============================
    for(int iSpec = 0; iSpec < vecSpecies.size(); iSpec++)
    {
        s1 = vecSpecies[iSpec];
        p1 = &( s1->particles );
        string group_name = "/" + s1->species_param.species_type;
        group_id = H5Gcreate(restore_file_id_, group_name.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);

        //write the variable "PtclNumber"
        partNumber = s1->getNbrOfParticles();
        dims[0]     = 1;
        count[0]    = 1;
        offset[0]   = 0;
        stride[0]   = 0;
        block[0]    = 0;
        dataspace_id = H5Screate_simple(RANK, dims, NULL);
        dataset_id = H5Dcreate2(group_id, "partNumber", H5T_NATIVE_INT, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
        status = H5Dwrite (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &partNumber);
        H5Sclose (dataspace_id);
        H5Dclose (dataset_id);

        //write bmin of species
        nbins = s1->bmin.size();
        dims[0]     = nbins;
        count[0]    = nbins;
        offset[0]   = 0;
        stride[0]   = 0;
        block[0]    = 0;
        dataspace_id = H5Screate_simple(RANK, dims, NULL);
        dataset_id = H5Dcreate2(group_id, "bmin", H5T_NATIVE_INT, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
        status = H5Dwrite (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(s1->bmin[0]) );
        H5Sclose (dataspace_id);
        H5Dclose (dataset_id);

        //write bmax of species
        nbins = s1->bmax.size();
        dims[0]     = nbins;
        count[0]    = nbins;
        offset[0]   = 0;
        stride[0]   = 0;
        block[0]    = 0;
        dataspace_id = H5Screate_simple(RANK, dims, NULL);
        dataset_id = H5Dcreate2(group_id, "bmax", H5T_NATIVE_INT, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
        status = H5Dwrite (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(s1->bmax[0]) );
        H5Sclose (dataspace_id);
        H5Dclose (dataset_id);

        //Write momentum(0,iPart)
        dims[0]     = partNumber;
        count[0]    = partNumber;
        offset[0]   = 0;
        stride[0]   = 0;
        block[0]    = 0;

        dataspace_id = H5Screate_simple(RANK, dims, NULL);
        dataset_id = H5Dcreate2(group_id, "momentum0", H5T_NATIVE_DOUBLE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
        status = H5Dwrite (dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(p1->momentum(0,0)) );
        H5Sclose (dataspace_id);
        H5Dclose (dataset_id);


        //Write momentum(1,iPart)
        dims[0]     = partNumber;
        count[0]    = partNumber;
        offset[0]   = 0;
        stride[0]   = 0;
        block[0]    = 0;

        dataspace_id = H5Screate_simple(RANK, dims, NULL);
        dataset_id = H5Dcreate2(group_id, "momentum1", H5T_NATIVE_DOUBLE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
        status = H5Dwrite (dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(p1->momentum(1,0)) );
        H5Sclose (dataspace_id);
        H5Dclose (dataset_id);


        //Write momentum(2,iPart)
        dims[0]     = partNumber;
        count[0]    = partNumber;
        offset[0]   = 0;
        stride[0]   = 0;
        block[0]    = 0;

        dataspace_id = H5Screate_simple(RANK, dims, NULL);
        dataset_id = H5Dcreate2(group_id, "momentum2", H5T_NATIVE_DOUBLE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
        status = H5Dwrite (dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(p1->momentum(2,0)) );
        H5Sclose (dataspace_id);
        H5Dclose (dataset_id);


        //Write position(0,iPart)
        dims[0]     = partNumber;
        count[0]    = partNumber;
        offset[0]   = 0;
        stride[0]   = 0;
        block[0]    = 0;

        dataspace_id = H5Screate_simple(RANK, dims, NULL);
        dataset_id = H5Dcreate2(group_id, "position0", H5T_NATIVE_DOUBLE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
        status = H5Dwrite (dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(p1->position(0,0)) );
        H5Sclose (dataspace_id);
        H5Dclose (dataset_id);

        if(params->geometry == "2d3v")
        {
            //Write position(1,iPart)
            dims[0]     = partNumber;
            count[0]    = partNumber;
            offset[0]   = 0;
            stride[0]   = 0;
            block[0]    = 0;

            dataspace_id = H5Screate_simple(RANK, dims, NULL);
            dataset_id = H5Dcreate2(group_id, "position1", H5T_NATIVE_INT, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
            status = H5Dwrite (dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(p1->position(1,0)) );
            H5Sclose (dataspace_id);
            H5Dclose (dataset_id);

        }
        H5Gclose (group_id);
    }
    H5Fclose (restore_file_id_);
}
*/

/* Create restore h5 file pattern
void IO::reloadP( PicParams* params_, ParallelMPI* pmpi_, vector<Species*>& vecSpecies )
{
    Species     *s1;
    Particles   *p1;
    hid_t       group_id;
    hid_t       dataspace_id;
    hid_t       dataset_id;
    hid_t       memspace_id;
    hid_t       attribute_id;
    hid_t       prop;
    herr_t      status;

    hsize_t     count[1]; 
    hsize_t     offset[1]; 
    hsize_t     stride[1];
    hsize_t     block[1];

    int restart;
    int partNumber;
    int partNumber_old;
    int nbins;
    int RANK;
    double data_temp;
    hsize_t dims[1];
    hsize_t dims_extented[1];
    hsize_t maxdims[1];
    hsize_t chunk_dims[1];

    RANK = 1;
    maxdims[0] = H5S_UNLIMITED;
    chunk_dims[0] = 2;
    restart = 0;
    data_temp = 66.0;


    //Create the directory "restore" if it not exists
    string dirName = restore_file_dir;
    if( access(dirName.c_str(), 0) ==-1 )
    {
        mkdir(dirName.c_str(), 0777);
    }


    long long mpi_rk = pmpi->getRank();
    string fileName = restore_file_dir + "/Restore" + to_string( mpi_rk ) + "_global.h5";
    if( access(fileName.c_str(), 0) ==-1 )  // The file not exists
    {
        return;
    }

    //Restore000_global.h5
    restore_file_id_  = H5Fopen( fileName.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
    if(restore_file_id_ < 0)
    {
        log_warning<<"Can not open restore file: restore_file_id_ = "<<restore_file_id_;
        return;
    }


    //======================= Read info about the restart ====================
    group_id = H5Gopen(restore_file_id_, "/info", H5P_DEFAULT);
    //Read the variable "timestep"
    dataset_id = H5Dopen(group_id, "timestep", H5P_DEFAULT);
    status = H5Dread (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &stepStart);
    H5Dclose( dataset_id );

    //Read the variable "restart"
    dataset_id = H5Dopen(group_id, "restart", H5P_DEFAULT);
    status = H5Dread (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &restart);
    H5Dclose (dataset_id);
    if(restart == 0 && params->is_continue == 0)
    {
        H5Gclose(group_id);
        H5Fclose(restore_file_id_);
        stepStart = 0;
        return;
    }
    else if(restart == 1 && params->is_continue == 0)
    {
        is_restart = 1;
    }
    else if(params->is_continue == 1)
    {
        is_restart = 0;
        stepStart  = 0;
    }


    //Read the variable "nSpecies"
    dataset_id = H5Dopen(group_id, "nSpecies", H5P_DEFAULT);
    status = H5Dread (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &nSpecies);
    H5Dclose( dataset_id );
    H5Gclose( group_id );

    //====================== Read all stored species ==========================
    for(int iSpec = 0; iSpec < nSpecies; iSpec++)
    {
        s1 = vecSpecies[iSpec];
        p1 = &(s1->particles);
        string group_name = "/" + s1->species_param.species_type;
        group_id = H5Gopen(restore_file_id_, group_name.c_str(), H5P_DEFAULT);

        //Read the variable "PtclNumber" =================================================
        dataset_id = H5Dopen(group_id, "partNumber", H5P_DEFAULT);
        status = H5Dread (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &partNumber);
        H5Dclose (dataset_id);

        //resize the particles with the partNumber
        p1->initialize(partNumber, params);


        //Read bmin ===================================================================
        dataset_id = H5Dopen(group_id, "bmin", H5P_DEFAULT);
        status = H5Dread (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(s1->bmin[0]) );
        H5Dclose (dataset_id);

        //Read bmax ===================================================================
        dataset_id = H5Dopen(group_id, "bmax", H5P_DEFAULT);
        status = H5Dread (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(s1->bmax[0]) );
        H5Dclose (dataset_id);

        //Read momentum(0,iPart) ======================================================
        dataset_id = H5Dopen(group_id, "momentum0", H5P_DEFAULT);
        status = H5Dread (dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(p1->momentum(0,0)) );
        H5Dclose (dataset_id);


        //Read momentum(1,iPart) ======================================================
        dataset_id = H5Dopen(group_id, "momentum1", H5P_DEFAULT);
        status = H5Dread (dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(p1->momentum(1,0)) );
        H5Dclose (dataset_id);



        //Read momentum(2,iPart)  ======================================================
        dataset_id = H5Dopen(group_id, "momentum2", H5P_DEFAULT);
        status = H5Dread (dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(p1->momentum(2,0)) );
        H5Dclose (dataset_id);


        //Read position(0,iPart)  ======================================================
        dataset_id = H5Dopen(group_id, "position0", H5P_DEFAULT);
        status = H5Dread (dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(p1->position(0,0)) );
        H5Dclose (dataset_id);

        if(params->geometry == "2d3v")
        {
            //Read position(1,iPart) ======================================================
            dataset_id = H5Dopen(group_id, "position1", H5P_DEFAULT);
            status = H5Dread (dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(p1->position(1,0)) );
            H5Dclose (dataset_id);

        }
        H5Gclose (group_id);
    }
    H5Fclose (restore_file_id_);

}
*/



/* Create restore h5 file pattern
void IO::endStoreP( PicParams* params_, ParallelMPI* pmpi_, vector<Species*>& vecSpecies, int itime )
{
    Species *s1;
    Particles *p1;
    hid_t       group_id;
    hid_t       dataspace_id;
    hid_t       dataset_id;
    hid_t       memspace_id;
    hid_t       attribute_id;
    hid_t       prop;
    herr_t      status;

    int restart;
    restart = 0;

    long long mpi_rk = pmpi->getRank();
    string fileName = restore_file_dir + "/Restore" + to_string( mpi_rk ) + "_global.h5";

    //Restore000_global.h5
    restore_file_id_  = H5Fopen( fileName.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
    string group_name = "/info";
    group_id = H5Gopen(restore_file_id_, group_name.c_str(), H5P_DEFAULT);

    //write the variable "restart" =========================================
    dataset_id = H5Dopen(group_id, "restart", H5P_DEFAULT);
    status = H5Dwrite (dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &restart);

    H5Dclose (dataset_id);
    H5Gclose (group_id);
    H5Fclose (restore_file_id_);
}
*/
